﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Remoting;
using GameServer;
using System.Threading;
using System.Runtime.Remoting.Channels;
using System.Runtime.Serialization.Formatters;
using System.Collections;
using FunMazeEngine;
using System.Runtime.Remoting.Channels.Tcp;
using System.Net;
using System.Net.Sockets;
using MazeEntity;

namespace FunMazeClient
{
    public partial class GameBoard : Form
    {
        Game gameInfo = null;
        GameEntity gameEntity = null;
        IMediator im = null;
        EventsClass client = null;
        string generalMessage = string.Empty;
        string backUpServerAddress = string.Empty;
        bool playerConnectGame = false;

        int tcpPort = 0;       
        TcpChannel remotingChannel = null;
        
     
        [STAThread]
        static void Main()
        {
            Application.Run(new GameBoard());
        }

        #region Form Control Helper Methods

        BackgroundWorker backWorker = new BackgroundWorker();

        void BackgroundWork()
        {
            backWorker.DoWork += new DoWorkEventHandler(BackgroundDoWork);
            backWorker.RunWorkerAsync();
            
        }

        void BackgroundDoWork(object sender, DoWorkEventArgs e)
        {
            txtGameInfoDisplay.Text = (string)(e.Argument);

        }

        void UpdateGameInfoDisplay(string message)
        {
            if (this.txtGameInfoDisplay.InvokeRequired)
            {
                this.BeginInvoke(new MethodInvoker(()=> UpdateGameInfoDisplay(message)));
            }
            else
            {
                this.txtGameInfoDisplay.Text += "\r\n" + message;
                this.txtGameInfoDisplay.SelectionStart = txtGameInfoDisplay.Text.Length;
                this.txtGameInfoDisplay.ScrollToCaret();
            }
        }

        void EnableGameControl(bool input)
        {
            if (this.grpGameControl.InvokeRequired)
            {
                this.BeginInvoke(new MethodInvoker(() => EnableGameControl(input)));
            }
            else
            {
                this.grpGameControl.Enabled = input;
            }
        }

        void EnableGameSettingPanel(bool input)
        {
            if (this.pnlGameSetting.InvokeRequired)
            {
                this.BeginInvoke(new MethodInvoker(() => EnableGameControl(input)));
            }
            else
            {
                this.pnlGameSetting.Enabled = input;
            }
        }

        void EnableGameSetting(bool input)
        {
            grpCreateNewGame.Enabled = input;
            grpGameOption.Enabled = input;
            grpJoinExistingGame.Enabled = input;
            txtPlayerName.Enabled = input;
        }

        #endregion

        public GameBoard()
        {
            InitializeComponent();
            gameInfo = new Game();
            gameEntity = new GameEntity();
            Random rd = new Random();
            tcpPort = rd.Next(5000, 7000);
        }        

        private void btnCreateGame_Click(object sender, EventArgs e)
        {
            if (txtGridSize.Text == "")
            {
                MessageBox.Show("Grid size is required field for new game creation");
                return;
            }
            else if (txtTreasureCount.Text == "")
            {
                MessageBox.Show("Treasure count is required field for new game creation");
                return;
            }
            else if (txtPlayerName.Text == "")
            {
                MessageBox.Show("Player name is required field for new game creation");
                return;
            }            

            Thread serverThread = new Thread(new ThreadStart(StartGameServer));
            serverThread.Start(); 
           
            ConnectToGameServer(this.GetIPAddress());

            UpdateGameInfoDisplay("You are hosting as primary server");
            CreateNewGame();
            if (playerConnectGame)
            {
                EnableGameSetting(false);
            }

            Thread gameStartThread = new Thread(new ThreadStart(StartGame));
            gameStartThread.Start();
            
        }

        private void btnJoinGame_Click(object sender, EventArgs e)
        {
            if (txtPlayerName.Text == "")
            {
                MessageBox.Show("Player name is required field for new game creation");
                return;
            }
            else if (txtGameServerIP.Text == "")
            {
                MessageBox.Show("Server address is required field to join existing creation");
                return;
            }
            else
            {
                ConnectToGameServer(txtGameServerIP.Text);
                JoinExistingGame();
            }

            if (playerConnectGame)
            {
                EnableGameSetting(false);
            }
        }

        private void btnQuitGame_Click(object sender, EventArgs e)
        {
            ReleaseHandlers();
            this.Dispose();            
        }

        private void GameBoard_FormClosed(object sender, FormClosedEventArgs e)
        {
            ReleaseHandlers();
            this.Dispose();
        }

        private void GameBoard_FormClosing(object sender, FormClosingEventArgs e)
        {
            ReleaseHandlers();
            this.Dispose();
        }

        private void btnNorth_Click(object sender, EventArgs e)
        {            
            PlayerMove('N');
        }

        private void btnEast_Click(object sender, EventArgs e)
        {
            PlayerMove('E');
        }

        private void btnWest_Click(object sender, EventArgs e)
        {
            PlayerMove('W');
        }

        private void btnSouth_Click(object sender, EventArgs e)
        {
            PlayerMove('S');
        }

        #region Event Handler

        public void em_ServerUpdateHandler(string serverIP)
        {
            this.backUpServerAddress = serverIP;          
        }

        public void em_WelcomeMessageHandler(string message)
        {
            generalMessage = message;
            UpdateGameInfoDisplay(generalMessage);            
        }

        public void em_PlayerLeftHandler(IGameClient client)
        {
            generalMessage = client.Player.Name + " is left the Game\r\n";
            UpdateGameInfoDisplay(generalMessage);

            //this.clientList = ici.ClientList;
            //Thread th = new Thread(new ThreadStart(this.GetClientList));
            //th.Start();
        }

        public void em_GameInfoUpdateHandler(Game theGame)
        {
            try
            {
                this.gameInfo = new Game();
                gameInfo = (Game)theGame;

                List<GamePlayerEntity> playerList = new List<GamePlayerEntity>();
                foreach (IGameClient client in gameInfo.PlayerList)
                {
                    GamePlayer player = client.Player;
                    GamePlayerEntity playerEntity = new GamePlayerEntity(player.Name);
                    playerEntity.Move = player.Move;
                    playerEntity.Location = player.Location;
                    playerEntity.IsPrimaryHost = player.IsPrimaryHost;
                    playerEntity.IsBackUpHost = player.IsBackUpHost;
                    playerEntity.IsAlive = player.IsAlive;
                    playerEntity.PlayerIPAddress = player.PlayerIPAddress;
                    playerEntity.TotalCollectedTreasure = player.TotalCollectedTreasure;
                    playerList.Add(playerEntity);
                }
                MapEntity mapEntity = new MapEntity();
                Map map = gameInfo.GameMap;
                mapEntity.cellNum = map.cellNum;
                mapEntity.columns = map.columns;
                mapEntity.map = map.map;
                mapEntity.rows = map.rows;
                mapEntity.treasureCount = map.treasureCount;
                mapEntity.treasureList = map.treasureList;

                gameEntity.occupiedlist = gameInfo.occupiedlist;

                gameEntity.GameMap = mapEntity;

                gameEntity.PlayerList = playerList;
            }
            catch
            {
                
                
            }
           
            
        }

        public void em_StartGameServerHandler(Game gameInfo)
        {
            Thread serverThread = new Thread(new ThreadStart(StartGameServer));
            serverThread.Start();
            UpdateGameInfoDisplay("You are hosting as back up server");
        }

        public bool em_PlayerAliveHandler()
        {
            return true;
        }

        private void ReleaseHandlers()
        {
            try
            {
                client.BackUpServerIPUpdateHandler -= new BackUpServerIPUpdate(this.em_ServerUpdateHandler);
                client.WelComeMessageHandler -= new WelComeMessage(this.em_WelcomeMessageHandler);
                client.PlayerLeftHandler -= new PlayerLeftGame(this.em_PlayerLeftHandler);
                client.GameInfoUpdateHandler -= new GameInfoUpdate(this.em_GameInfoUpdateHandler);
                client.StartGameServerHandler -= new StartGameServer(this.em_StartGameServerHandler);
                client.PlayerAliveHandler -= new FunMazeClient.PlayerAlive(this.em_PlayerAliveHandler);
            }
            catch
            {
                return;
            }
        }

        #endregion
        
        #region Local Methods       

        private void PlayerMove(char move)
        {
            try
            {
                if (playerConnectGame)
                {
                    int result = im.PlayerMove(this.client, move);
                    if (result == 2) { txtGameInfoDisplay.Clear(); }
                    else if (result == 1) { UpdateGameInfoDisplay("You are hitting other player and your movement is not allow."); }
                    else if (result != -1) { UpdateGameInfoDisplay("You are hitting to well"); }                    
                }
                else
                {
                    UpdateGameInfoDisplay("There has no game created or joined by you, please start name game or join existing game");                    
                }             
            }
            catch (SocketException)
            {
                UpdateGameInfoDisplay("Game server switcing");
                if (playerConnectGame)
                {
                    ConnectBackUpServer();
                }
            }
            catch (RemotingException)
            {
                UpdateGameInfoDisplay("Game server switcing");
                if (playerConnectGame)
                {
                    ConnectBackUpServer();
                }
            }
            catch (Exception)
            {
                UpdateGameInfoDisplay("Game server switcing");
                if (playerConnectGame)
                {
                    ConnectBackUpServer();
                }
            }  
        }

        private void ConnectBackUpServer()
        {
            try
            {
                ConnectToGameServer(backUpServerAddress);
                if (im != null)
                {                    
                    if(client.Player.PlayerIPAddress.Substring(1,14) == backUpServerAddress.Substring(1,14))
                    {
                        im.SwitchToPrimaryServer(gameEntity, this.client);
                        UpdateGameInfoDisplay("You are hosting as primary server");
                    }
                    else
                    {
                        im.UpdateClientList(this.client);
                    }
                }                  
            }
            catch (Exception ex)
            {
                UpdateGameInfoDisplay("Error on switching game server : " + ex.StackTrace.ToString());
            }
         
        }

        private string GetIPAddress()
        {
            string localIPAddress = string.Empty;
            IPHostEntry host;
            host = Dns.GetHostEntry(Dns.GetHostName());

            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    localIPAddress = ip.ToString();
                }
            }

            
            localIPAddress = localIPAddress + ":" + tcpPort.ToString();
            return localIPAddress;
        }

        private void StartGame()
        {
            if (!this.im.Equals(null))
            {
                playerConnectGame = im.StartGame();
                EnableGameControl(true);
            }
        }

        private void StartGameServer()
        {
            try
            {
                Server server = new Server(tcpPort);
                RemotingConfiguration.RegisterWellKnownServiceType(typeof(Mediator), "GameServer.soap",
                    WellKnownObjectMode.Singleton);

            }
            catch (RemotingException ex)
            {
                Console.WriteLine(ex.Message);           
            }
        }

        private void ConnectToGameServer(string serverIPAddress)
        {
            try
            {
                if (remotingChannel != null)
                {
                    ChannelServices.UnregisterChannel(remotingChannel);
                }

                BinaryClientFormatterSinkProvider bcfs = new BinaryClientFormatterSinkProvider();
                BinaryServerFormatterSinkProvider bsfs = new BinaryServerFormatterSinkProvider();
                bsfs.TypeFilterLevel = TypeFilterLevel.Full;

                IDictionary id = new Hashtable();

                id = new Hashtable();
                id["port"] = 0;
                id["typeFilterLevel"] = TypeFilterLevel.Full;
                id["name"] = System.Guid.NewGuid().ToString();

                
                string tcpServerUrl = "tcp://" + serverIPAddress + "/GameServer.soap";

                remotingChannel = new TcpChannel(id, bcfs, bsfs);
                ChannelServices.RegisterChannel(remotingChannel, false);
                this.im = (IMediator)Activator.GetObject(typeof(IMediator), tcpServerUrl);

            }
            catch (RemotingException ex)
            {
                txtGameInfoDisplay.Text = ex.Message + " Stack trace : " + ex.StackTrace;
            }
            catch (Exception)
            {
                UpdateGameInfoDisplay("Unable to connect to game server");
            }
        }

        private void CreateNewGame()
        {
            if (this.im != null)
            {
                client = new EventsClass();

                client.BackUpServerIPUpdateHandler += new BackUpServerIPUpdate(this.em_ServerUpdateHandler);
                client.WelComeMessageHandler += new WelComeMessage(this.em_WelcomeMessageHandler);
                client.PlayerLeftHandler += new PlayerLeftGame(this.em_PlayerLeftHandler);
                client.GameInfoUpdateHandler += new GameInfoUpdate(this.em_GameInfoUpdateHandler);
                client.StartGameServerHandler += new StartGameServer(this.em_StartGameServerHandler);
                client.PlayerAliveHandler += new FunMazeClient.PlayerAlive(this.em_PlayerAliveHandler);

                GamePlayer player = new GamePlayer(txtPlayerName.Text);
                player.PlayerIPAddress = GetIPAddress();
                player.IsPrimaryHost = true;
                client.Player = player;

                playerConnectGame = im.CreateGame(client, int.Parse(txtGridSize.Text), int.Parse(txtTreasureCount.Text));

                if (playerConnectGame)
                {
                    UpdateGameInfoDisplay("Game create successfully in Server : " + this.GetIPAddress());
                    UpdateGameInfoDisplay("Player : " + player.Name + " is added to game at the location of " + player.Location.ToString());
                    UpdateGameInfoDisplay("Waiting to start game....");
                }

            }
        }

        private void JoinExistingGame()
        {
            if (this.im != null)
            {
                try
                {
                    client = new EventsClass();

                    client.BackUpServerIPUpdateHandler += new BackUpServerIPUpdate(this.em_ServerUpdateHandler);
                    client.WelComeMessageHandler += new WelComeMessage(this.em_WelcomeMessageHandler);
                    client.PlayerLeftHandler += new PlayerLeftGame(this.em_PlayerLeftHandler);
                    client.GameInfoUpdateHandler += new GameInfoUpdate(this.em_GameInfoUpdateHandler);
                    client.StartGameServerHandler += new StartGameServer(this.em_StartGameServerHandler);
                    client.PlayerAliveHandler += new FunMazeClient.PlayerAlive(this.em_PlayerAliveHandler);

                    GamePlayer player = new GamePlayer(txtPlayerName.Text);
                    player.PlayerIPAddress = GetIPAddress();
                    client.Player = player;

                    playerConnectGame = im.JoinGame(client);
                }
                catch (Exception ex)
                {
                    UpdateGameInfoDisplay("Unable to join the Game due to : " + ex.Message);
                }                                              
            }
        }

        #endregion

        private void rdoNewGame_CheckedChanged(object sender, EventArgs e)
        {
            if (rdoNewGame.Checked)
            {
                grpCreateNewGame.Enabled = true;
                grpJoinExistingGame.Enabled = false;
            }
        }

        private void rdoJoinGame_CheckedChanged(object sender, EventArgs e)
        {
            if (rdoJoinGame.Checked)
            {
                grpCreateNewGame.Enabled = false;
                grpJoinExistingGame.Enabled = true;
            }
        }

       
    }
}
